package main

import (
	"common/registry"
	"common/rtpengine/rtp"
	"common/web"
	"encoding/json"
	"flag"
	"github.com/pterm/pterm"
	"net"
	"os"
	"strconv"
	"time"
	"xmediaEmu/pkg/cws"
	"xmediaEmu/pkg/cws/entity"
	"xmediaEmu/pkg/emulator/rtpua"
	"xmediaEmu/pkg/log"
	"xmediaEmu/pkg/media/h264writer"
)

var (
	ServiceName    = ""
	VideoFileNames = ""

	ClientLocalAddr = "10.153.90.4:16666"
)

func init() {
	serviceName := flag.String("url", "", "The service name for test")
	fileNames := flag.String("f", "", "The filename list")
	flag.Parse()

	if serviceName != nil {
		ServiceName = *serviceName
	}
	if fileNames != nil {
		VideoFileNames = *fileNames
	}
}

// WsSender 发送byte.
type EmuClient struct {
	serviceName string
	wsClient    *web.WSocket
	cwsClient   *cws.Client
	stop        chan bool
	sequence    int
	roomId      string
}

// ConnectEmu service name
// serviceName = com.cmic.xmediaemu
func ConnectEmu(serviceName string, stop chan bool) *EmuClient {
	prefixName := "/xmode/micro-registry"
	_ = registry.DefaultRegistry.Init(registry.Addrs("10.153.90.4:2379"), registry.Prefix(prefixName))

	//destUrl, err := GetWsUrlFromRegistry(serviceName)
	//pterm.FgWhite.Printfln("destUrl is %q, registry prefix:%q, err:%v. ", destUrl, prefixName, err)
	ws := &EmuClient{serviceName: serviceName, wsClient: web.NewWithRegistry(serviceName)}
	// ws := &EmuClient{serviceName: serviceName, wsClient: web.New(serviceName)}
	ws.wsClient.OnConnected = func(socket web.WSocket) {
		pterm.FgWhite.Printfln("Connected to %q service failed, registry prefix:%q. ", serviceName, prefixName)
	}

	ws.stop = stop
	ws.cwsClient = cws.NewClient("testWorkServer", ws.wsClient)
	// 注册回调函数处理响应.
	ws.cwsClient.Receive(entity.RoomStarted, ws.OnHandleRoomStart())
	// ws.cwsClient.Receive(entity.RegisterRoom, ws.OnHandleRoomStart())

	// 开启异步接收协程
	err := ws.cwsClient.Connect()
	if !ws.wsClient.IsConnected {
		pterm.FgRed.Printfln("The ws url %q connect fail:%q ", ws.wsClient.Url, err)
		os.Exit(-1)
	}
	return ws
}

func (ws *EmuClient) OnHandleRoomClose() cws.PacketHandler {
	return func(resp cws.WSPacket) (req cws.WSPacket) {
		pterm.FgRed.Println("OnHandleRoomClose done")

		// 不用回啥响应
		return cws.EmptyPacket
	}
}

func (ws *EmuClient) Send(command, data string) error {
	// callback must be nil
	ws.sequence++
	ws.cwsClient.Send(cws.WSPacket{ID: command, Data: data, PacketID: strconv.Itoa(ws.sequence), SessionID: "123456890117"}, nil)
	return nil
}

func (ws *EmuClient) Close() {
	// ws.wsClient.SendBinary(websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
	ws.wsClient.Close()
}

// OnHandleRoomStart 处理roomStart响应
func (ws *EmuClient) OnHandleRoomStart() cws.PacketHandler {
	return func(resp cws.WSPacket) (req cws.WSPacket) {
		// resp返回，获取sdp.
		rom := entity.RoomStartRsp{}
		if err := rom.From(resp.Data); err != nil {
			pterm.FgRed.Printfln("OnHandleRoomStart rom.From error: %v", err)
			rom.Status = 500
			return cws.EmptyPacket
		}

		// RoomStartRsp假如错误就不不返回消息,
		if rom.Status > 200 {
			pterm.FgRed.Printfln("OnHandleRoomStart rom.From error: %d", rom.Status)
			return cws.EmptyPacket
		}

		ws.roomId = rom.RoomId
		pterm.FgWhite.Printfln("OnHandleRoomStart response id: %s sdp: %q", rom.RoomId, rom.Sdp)
		var sdp rtpua.Sdp
		if err := json.Unmarshal([]byte(rom.Sdp), &sdp); err != nil {
			pterm.FgRed.Printfln("OnHandleRoomStart json.Unmarshal error: %v", err)
			return cws.EmptyPacket
		}
		_ = ws.startVideoRtpReceive(sdp)

		// 不用回啥响应
		return cws.EmptyPacket
	}
}

// 接收视频video流10s.
// TODO:fps不足问题。实时播放时没问题，离线保存播放时有问题.
func (ws *EmuClient) startVideoRtpReceive(sdp rtpua.Sdp) error {
	// 40 s文件接收处理.
	ticker := time.NewTicker(time.Second * 40)
	// timeTicker := time.NewTicker(time.Second)
	// counter := 0

	// 创建rtp流.
	localAddr, _ := net.ResolveUDPAddr("udp", ClientLocalAddr)
	conn, err := net.ListenUDP("udp", localAddr)
	if err != nil {
		pterm.FgRed.Printf("Listen the remote addr fail:%v... \n", err)
		os.Exit(-1)
	}
	_ = conn.SetReadBuffer(5242880)
	defer conn.Close()
	pterm.FgWhite.Printf("startVideoRtpReceive %s\n", localAddr)

	// create h264 file.
	h264File, err := h264writer.New(VideoFileNames)
	if err != nil {
		panic(err)
	}
	defer h264File.Close()
	packet := rtp.Packet{}

	var currentBytes int
	var temp [1500]byte
	for {
		select {
		case <-ticker.C:
			// 20 second 到了停止.
			ws.stop <- true
			return nil
		default:
			// 无限循环.
			// 默认每次读到1200个字节.
			receiveBytes, _, err := conn.ReadFromUDP(temp[0:])
			if err != nil {
				pterm.FgLightRed.Printf("Receive the message fail, the error is %s\n", err.Error())
				return err
			}
			if err := packet.Unmarshal(temp[0:receiveBytes]); err != nil {
				pterm.FgLightRed.Printf("receiveLocalMessage: Unmarshal failed:%v\n. ", err)
			} else {
				if err = h264File.WriteRTP(&packet); err != nil {
					pterm.FgLightRed.Printf("receiveLocalMessage: WriteRTP failed:%v\n. ", err)
				} else {
					pterm.FgWhite.Printf("hWriter.WriteFile count:%d done. \n", receiveBytes)
				}
			}

			// 接收完decode.
			currentBytes += receiveBytes
			// counter++
			pterm.FgWhite.Printf("Receive the message success, the total length is %d\n", currentBytes)

			if currentBytes >= 1000 && currentBytes < 3000 {
				rom := entity.RoomTextMessage{Text: "hello chromedp: " + strconv.Itoa(currentBytes)}
				data, err := rom.To()
				if err = ws.Send(entity.RoomText, data); err != nil {
					pterm.FgRed.Printfln("sender.Send text fail: %v", err)
					os.Exit(-5)
				}
				log.Logger.Info("startVideoRtpReceive hello chromedp" + strconv.Itoa(currentBytes))
			}
		}
	}
	return nil
}

// 模拟xmedia分发到client.
// 转成h264流后给到ffmpeg读取播放.
// act as ws client.
func main() {
	log.Init("1", log.Config{Level: int32(0), Dir: "./", Path: "coordinator.log", FileNum: 10})

	if ServiceName == "" {
		ServiceName = "com.cmic.xmediaemu/room"
	}

	if VideoFileNames == "" {
		VideoFileNames = "coordinator.h264"
	}
	done := make(chan bool, 1)
	sender := ConnectEmu(ServiceName, done)
	defer sender.Close()

	start := time.Now()
	pterm.FgWhite.Printfln("The %q file start receiving from ws\n", VideoFileNames)
	//waitGroup.Wait()
	// 创建房间交互 Begin:================================================================================================
	// text or image or chromedp
	rom := entity.RoomStartCall{Name: "chromedp", AudioPayloadType: 97, VideoPayloadType: 101, Zone: "udp", Addr: ClientLocalAddr}
	data, err := rom.To()
	if err = sender.Send(entity.RoomStart, data); err != nil {
		pterm.FgRed.Printfln("sender.Send fail: %v", err)
		os.Exit(-3)
	}
	// 创建房间交互 End:================================================================================================
	<-done

	// 发送退出房间命令.
	romQuit := entity.QuitRoomPacket(sender.roomId)
	romQuit.PacketID = "2"
	romQuit.SessionID = "123456890117"
	sender.cwsClient.Send(romQuit, nil)

	pterm.FgLightYellow.Printfln("The %s file end sending, result is %v, duration is %f\n", VideoFileNames, err, time.Since(start).Seconds())
	time.Sleep(time.Second * 2)
	// }
}
